/**
 * 此类用于对接热更新升级数据
 * 公共方法
 * 
 * 1, 访问热更新服务器, 获取资源升级数据
 * 2, 下载+解压
 * 3, 回传展示数据
 */

import Http, { HttpType } from "../Net/Http";

export class AssetsData {
    url: string = '';
    md5: string = '';
    atv: string = '';
    ver: string = '';
    size: number = 0;
    public SetAtv(atv: string) {
        this.atv = atv;
        this.ver = JSON.parse(this.atv).ver;
    }
}

export enum UpgradeEvent {
    Nothing = "Nothing",        //无更新
    Pkg = "UpgradePackage",     //更新底包
    Assets = "UpgradeAssets",   //更新资源
}

const { ccclass, property } = cc._decorator;
@ccclass
export default abstract class AUpgrade extends cc.Component {
    protected readonly aClass = ""; //apk 安装函数

    protected mExpected: number = 0;    //需要接收的总字节数
    protected mReceived: number = 0;    //已经接收的字节数
    protected mAssetsList: Array<AssetsData> = null;
    protected mIdx: number = 0;  //当前下载的资源下标

    protected onDestroy(): void {
        if (this.mAssetsList != null) {
            for (let i = 0; i < this.mAssetsList.length; i++) {
                this.RemoveCallback(this.mAssetsList[i].url);
            }
        }
    }

    protected Analysis(cb: Function, url: string, param: object) {
        let self = this;
        console.log(`[AUpgrade] Analysis url = ${url} param = ${JSON.stringify(param)}`);

        let analysis = (status, result) => {
            console.log("[upgrade].result :", JSON.stringify(param), JSON.stringify(result));
            if (!cc.sys.isNative) {
                cb(UpgradeEvent.Nothing);
                return;
            }  //web环境就可以滚开了

            switch (status) {
                case 200:
                    let res = JSON.parse(result);
                    if (res.code == 0)   //没有错误
                    {
                        let data = res.data;
                        if (data == null || data == undefined)//审核状态直接退出
                        {
                            cb(UpgradeEvent.Nothing);  //没有发生更新操作
                            return;
                        }
                        else if (data.exa) {
                            cb(UpgradeEvent.Nothing);  //没有发生更新操作
                            return;
                        }

                        if (data.icu)    //更新apk
                        {
                            cb(UpgradeEvent.Pkg, data.curl, data.cmd5);
                        }
                        else if (data.iau)   //热更新资源
                        {
                            cb(UpgradeEvent.Assets, data.al);
                        }
                        else {
                            cb(UpgradeEvent.Nothing);  //没有发生更新操作
                        }
                    }
                    else {
                        console.error("[Upgrade.Go] data error");
                    }
                    break;
                default:
                    console.error("[Upgrade.Go] fail");
                    self.Analysis(cb, url, param);
                    break;
            }
        };
        Http.obj.Send(url, analysis, Http.obj.ParamSplicing(param), HttpType.POST);
    }

    protected ClearData() {
        this.mExpected = 0;
        this.mReceived = 0;
        this.mAssetsList = new Array<AssetsData>();
        this.mIdx = 0;
    }

    protected Decompress(verTxt: string, zipPath: string, targetPath: string) //解压缩
    {
        let isSuccess = jsb.fileUtils.Decompress(zipPath, targetPath);   //解压功能
        console.log(`[AUpgrade] Decompress isSuccess = ` + isSuccess);
        if (isSuccess) {
            jsb.fileUtils.removeFile(zipPath);          //解压完毕之后删掉压缩包
            console.log(`[AUpgrade] Decompress this.VerPath = ` + this.VerPath);
            jsb.fileUtils.writeStringToFile(verTxt, jsb.fileUtils.getWritablePath() + this.VerPath);
            this.mIdx++;
            if (this.mIdx < this.mAssetsList.length) {
                this.DownloadAssets();
            }
            else {
                this.OnDecompressDone();
            }
        }
        else {
            let self = this;
            this.scheduleOnce(() => {
                self.Decompress(verTxt, zipPath, targetPath);
            }, 1);
        }
    }

    protected OnError(storagePath, errorCode, errorCodeInternal, errorStr) {
        //错误信息, 下载检测后发现md5不对的情况下 需要删除原有的 storagePath 然后重新下载
        console.log("[Upgrade].[OnError].storagePath: " + storagePath)
        console.log("[Upgrade].[OnError].errorCode: " + errorCode)
        console.log("[Upgrade].[OnError].errorCodeInternal: " + errorCodeInternal)
        console.log("[Upgrade].[OnError].errorStr: " + errorStr)
    }

    protected OnSucceed(storagePath: string, status: string) {
        if (status == "Succeed") {
            let data = this.mAssetsList[this.mIdx];
            let decompressPath = jsb.fileUtils.getWritablePath() + "cache/";
            this.Decompress(data.atv, storagePath, decompressPath);
        }
    }

    protected downloadProgress(storagePath, bytesReceived, totalBytesReceived, totalBytesExpected) {
        this.mReceived += bytesReceived;
        this.OnProgress();
    }

    protected Apk(url: string, md5: string) {
        this.ClearData();
        let self = this;
        let name = jsb.fileUtils.getWritablePath() + Math.floor(Math.random() * 1000000).toFixed(0) + ".apk"
        this.Download(url, name, md5, (storagePath: string, status: string) => {
            if (status == "Succeed") {
                self.scheduleOnce(() => {
                    jsb.reflection.callStaticMethod("lib/general/Device", "InstallAPK", "(Ljava/lang/String;)V", storagePath);
                }, 2);
            }
        }, true, this.OnError.bind(this), this.downloadProgress.bind(this));
    }

    protected Ipa(url: string) {
        console.log("ipa url: " + url);
        cc.sys.openURL(url);
    }

    protected Assets(al: Array<any>) {
        this.ClearData();
        for (let i = 0; i < al.length; i++) {
            let ele = al[i];
            this.mExpected += Number(ele.size);
            let ad: AssetsData = new AssetsData();
            ad.url = ele.aurl;
            ad.md5 = ele.amd5;
            ad.size = ele.size;
            ad.SetAtv(ele.atv);
            this.mAssetsList.push(ad);
        }
    }

    protected DownloadAssets() {
        let ad: AssetsData = this.mAssetsList[this.mIdx];
        let storagePath = this.storagePath(ad.ver);
        this.Download(ad.url, storagePath, ad.md5, this.OnSucceed.bind(this), true, this.OnError.bind(this), this.downloadProgress.bind(this));
    }

    protected Version(): string {
        if (cc.sys.isNative && jsb.fileUtils.isFileExist(this.VerPath)) {
            let text = jsb.fileUtils.getStringFromFile(jsb.fileUtils.getWritablePath() + this.VerPath);
            let json = JSON.parse(text);
            return json.ver;
        }
        else {
            return "0";
        }
    }

    protected abstract OnProgress();
    protected abstract OnDecompressDone();
    protected abstract get VerPath(): string;
    protected abstract storagePath(ver: string): string;

    protected abstract RemoveCallback(url: string);
    protected abstract Download(url: string, fullName: string, md5: string, onSucceed: Function, retry?: boolean, onError?: Function, onProgress?: Function);
}